#ifndef _CHAINOFRESPONSIBILITY_HPP__
#define _CHAINOFRESPONSIBILITY_HPP__

//责任链模式代码
#include <boost/mpl/bool_fwd.hpp>
#include<iostream>
#include<string>
#include<unistd.h>
#include<memory>
#include<ctime>
#include<sstream>
#include<filesystem>
#include<fstream>
#include<sys/types.h>
#include<sys/wait.h>

class HandlerText
{
public:
    HandlerText():_enable(true){}
    virtual ~HandlerText() = default;

    //执行函数设为纯虚函数
    virtual void Execute(const std::string &text) = 0;
    //设置链式结构的下一节点
    void SetNext(const std::shared_ptr<HandlerText> next) {_next = next;}

    //该任务模块是否使能
    void Enable(){_enable = true;}
    void Disable(){_enable = false;}
    //判断该模块是否开启
    bool IsEnable(){return _enable;}
protected:
    std::shared_ptr<HandlerText> _next;
    bool _enable;
};

//任务1：格式化内容，加入时间和进程pid
class HandlerTextFormat:public HandlerText
{
public:
    HandlerTextFormat()
    {
        _pid = getpid();

        std::time_t t = std::time(nullptr);
        std::tm* ptm = std::localtime(&t); 

        std::snprintf(_buffer, sizeof(_buffer),
                  "%04d-%02d-%02d %02d:%02d:%02d",
                  ptm->tm_year + 1900,
                  ptm->tm_mon + 1,
                  ptm->tm_mday,
                  ptm->tm_hour,
                  ptm->tm_min,
                  ptm->tm_sec);
    }
    void Execute(const std::string &text) override
    {
        std::string results;
        if(IsEnable())
        {
            std::cout << "开始格式化..." << std::endl;

            std::stringstream ss;
            ss << "[" << _buffer << "]" << " - " << _pid << " - " << text << "\n";
            results = ss.str();
            std::cout << "step1 : 格式化消息： " << text << ", : " << results << std::endl;
            sleep(1);
        }
        else {results = text + "\n";}
        
        if(_next)
        {
            _next->Execute(results);
        }
        else
        {
            std::cout << "责任链节点结束，任务处理完成" << std::endl;
        }
    }
private:
    pid_t _pid;
    char _buffer[20];//储存当前时间
};

std::string defaultfilepath = "./tmp/";
std::string defaultfilename = "text.log";

//任务2：保存内容到文件中
class HandlerTextSaveFile:public HandlerText
{
public:
    HandlerTextSaveFile(const std::string &filepath = defaultfilepath,const std::string &filename = defaultfilename)
                        :_filepath(filepath)
                        ,_filename(filename)
    {
        //利用c++17中filesystem判断目录是否存在
        if (std::filesystem::exists(_filepath))
            return;
        try
        {
            std::filesystem::create_directories(_filepath);
        }
        catch (std::filesystem::filesystem_error &e)
        {
            std::cerr << e.what() << std::endl;
        }
    }

    void Execute(const std::string &text) override
    {
        if(IsEnable())
        {
            std::cout << "开始保存到文件中..." << std::endl;
            sleep(1);

            std::string file = _filepath + _filename;
            std::ofstream out(file,std::ios::app);
            if(!out.is_open()) return;
            out << text;
            out.close();
            std::cout << "step2 : 保存消息： " << text << "到： " << file << std::endl;

        }
        
        if(_next)
        {
            _next->Execute(text);
        }
        else
        {
            std::cout << "责任链节点结束，任务处理完成" << std::endl;
        }
    }
private:
    std::string _filepath;
    std::string _filename;
};


const int maxline = 5; // 为了尽快触发备份动作，该值设置⼩⼀些

//任务3：对过大的内容进行切片并保存,自动形成备份包，并删除备份文件
class HandlerTextBackUp:public HandlerText
{
public:
    HandlerTextBackUp(const std::string &filepath = defaultfilepath,const std::string &filename = defaultfilename)
                        :_filepath(filepath)
                        ,_filename(filename)
                        ,_maxline(maxline)
    {}


    void Execute(const std::string &text) override
    {
        if(IsEnable())
        {
            std::cout << "开始切片备份..." << std::endl;
            sleep(1);

            //1.打开文件
            std::string file = _filepath + _filename; 

            //2.备份
            if(OutOfRange(file))
            {
                std::cout << "消息⾏数超过" << _maxline << std::endl;
                Backup(file);
            }

        }
        
        if(_next)
        {
            _next->Execute(text);
        }
        else
        {
            std::cout << "责任链节点结束，任务处理完成" << std::endl;
        }
    }

    bool OutOfRange(const std::string &file) 
    {
        std::ifstream in(file);
        if(!in.is_open()) return false;

        std::string line;
        int curlinenum = 0;
        while(std::getline(in,line)) curlinenum++;
        in.close();
        return curlinenum > _maxline;
    }

    void Backup(const std::string &file)
    {
        //备份后缀
        std::string suffix = std::to_string(time(nullptr));
        //源文件
        std::string src_file = _filename + "." + suffix;
        //打包文件
        std::string tar_file = src_file + ".tgz";
        //备份文件
        std::string backup_file = file + "." + suffix;


        pid_t pid = fork();
        if(pid == 0)
        {
            //child
            //1.先对文件进行重命名，linux上对文件重命名是原子的
            //2.我们想让子进程进行数据备份
            std::filesystem::rename(file,backup_file);
            std::cout << "step3 : 备份消息： " << file << "->" << backup_file << std::endl;
            //3.对备份文件进行打包，打包成.tgz,需要使用exec*系统调用
            //若我们打包时时带路径打包，加压后会形成目录下带文件，需要我们进入目录才可以看到解压文件，所以进行命名更改
            //3.1更改路径打包不带文件路径
            std::filesystem::current_path(_filepath);
            //3.2系统调用tar打包
            execlp("tar","tar","-czf",tar_file.c_str(),src_file.c_str(),nullptr);
            //由于系统调用切换会使后续代码无法执行，所以只能由父进程删除备份文件
            exit(1);//切换失败返回1
        }

        //parent
        int status;
        pid_t rid = waitpid(pid,&status,0);
        if(rid > 0)
        {
           if(WIFEXITED(status) && WEXITSTATUS(status) == 0)
           {
                //打包成功，删除备份文件
                std::filesystem::remove(backup_file);
           }
        }
        else
            waitpid(pid, nullptr, 0);
    }

private:
    int _maxline;
    std::string _filepath;
    std::string _filename;
};

//责任链模式入口函数
class HanderEntry
{
public:
    HanderEntry()
    {
        _format = std::make_shared<HandlerTextFormat>();
        _save = std::make_shared<HandlerTextSaveFile>();
        _backup = std::make_shared<HandlerTextBackUp>();

        //通过指针进行链式连接
        _format->SetNext(_save);
        _save->SetNext(_backup);
    }
    
    //Start
    void Run(const std::string &text)
    {
        _format->Execute(text);
    }

    //设置任务使能，禁用或解锁任务
    void EnableHandler(bool isformat, bool issave, bool isbackup)
    {
        isformat ? _format->Enable() : _format->Disable();
        issave ? _save->Enable() : _save->Disable();
        isbackup ? _backup->Enable() : _backup->Disable();
    }
    ~HanderEntry(){}
private:
    std::shared_ptr<HandlerText> _format;
    std::shared_ptr<HandlerText> _save;
    std::shared_ptr<HandlerText> _backup;

};
#endif //ChainOfResponsibility.hpp